/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2013.12.10                                          *
 *                                                                       *
 *************************************************************************/

#ifndef FreeMAPs_include_COMMON_tbox_LogMessage_h
#define FreeMAPs_include_COMMON_tbox_LogMessage_h

#include "FreeMAPsConfig.h"

//SAMRAI headers
#ifdef FREEMAPS_HAVE_SAMRAI
#  include "SAMRAI/hier/PatchHierarchy.h"
#endif

//COMMON headers
#include "COMMON/base/Common.h"
#include "COMMON/tbox/InputDatabase.h"


namespace FreeMAPs
{

/**
 * tab value used to tells fill how many spaces when insert a tab key.
 * now default to 4
 */
const unsigned short ONE_TAB   = 4;
const unsigned short TWO_TAB   = ONE_TAB * 2;
const unsigned short THREE_TAB = ONE_TAB * 3;
const unsigned short FOUR_TAB  = ONE_TAB * 4;

class LogMessage
{
public:

    /**
     * when error occurs, use this function to get the
     * where the error is in the input file.
     */
    static std::string
    get_line_number_message (
        const InputDatabase* db,
        const std::string& key);

    /**
     * when error occurs, use this function to get the
     * where the error is in the input file.
     * no key is used, so return the db's key line number.
     */
    static std::string
    get_line_number_message (
        const InputDatabase* db);

    /**
     * key does have a FREEMAPS_DEFAULT_VALUE
     */
    static void
    disallow_using_default_value(
        const InputDatabase* db,
        const std::string& key);

    /**
     * specified key must be set and can not miss.
     */
    static void
    disallow_missing(
        const InputDatabase* db,
        const std::string& key);

    /**
     * key's value as a string array can not have space string.
     */
    static void
    disallow_using_blank_string(
        const InputDatabase* db,
        const std::string& key);

    /**
     * specified key should be a single value instead of array.
     */
    static void
    disallow_using_array(
        const InputDatabase* db,
        const std::string& key);

    /**
     * key's type is not allowable and it must be given allowable type
     */
    static void
    disallow_type(
        const InputDatabase* db,
        const std::string& key,
        const std::string& allowable_type);

    /**
     * key's type is not allowable and it can be given allowable types
     */
    static void
    disallow_type(
        const InputDatabase* db,
        const std::string& key,
        const std::set<std::string>& allowable_types);

    /**
     * check mutually exclusive of key_1 and key_2,
     * that means they can not set together.
     */
    static void
    disallow_mutually_exclusive(
        const InputDatabase* db,
        const std::string& key_1,
        const std::string& key_2);

    /**
     * a string type value is not supported. supported values will be tipped
     */
    static void
    use_improper_string_value(
        const InputDatabase* db,
        const std::string& key,
        const std::string& tip_info,
        const std::set<std::string>& allowable_values);

    /**
     * a string type value is not supported. supported values will be tipped
     */
    static void
    use_improper_string_value(
        const InputDatabase* db,
        const std::string& key,
        const std::string& value,
        const std::string& error_message);

    /**
     * a string vector's num item value is not supported. supported values will be tipped
     */
    static void
    use_improper_string_value(
        const InputDatabase* db,
        const std::string& key,
        const std::string& value,
        const std::string& tip_info,
        const std::set<std::string>& allowable_values);

    /**
     * a string vector's num item value is not supported. supported values will be tipped
     */
    static void
    use_improper_string_value(
        const InputDatabase* db,
        const std::string& key,
        const int num,
        const std::string& tip_info,
        const std::set<std::string>& allowable_values);

    /**
     * a string vector's num item value is not supported. supported values will be tipped
     */
    static void
    use_improper_string_value(
        const InputDatabase* db,
        const std::string& key,
        const int num,
        const std::string& error_message);

    static void
    use_improper_string_value_as_map_key(
        const InputDatabase* db,
        const std::string& key,
        const std::string& value,
        const std::string& tip_info,
        const std::set<std::string>& allowable_values);

    static void
    use_improper_string_value_as_map_value(
        const InputDatabase* db,
        const std::string& key,
        const std::string& value,
        const std::string& tip_info,
        const std::set<std::string>& allowable_values);

    static void
    use_improper_string_value_in_map(
        const InputDatabase* db,
        const std::string& key,
        const std::string& value,
        const std::string& tip_info,
        const std::set<std::string>& allowable_values);

    /**
     * a integer type value is not supported.
     */
    static void
    use_improper_integer_value(
        const InputDatabase* db,
        const std::string& key,
        const int value,
        const std::string& error_message);

    static void
    use_improper_integer_value(
        const InputDatabase* db,
        const std::string& key,
        const int num,
        const std::string& tip_info,
        const std::set<int>& allowable_values);

    /**
     * a double type value is not supported.
     */
    static void
    use_improper_double_value(
        const InputDatabase* db,
        const std::string& key,
        const double value,
        const std::string& error_message);

    /**
     * two vector have different size while this is not allowable.
     */
    static void
    disallow_different_size (
        const InputDatabase* db,
        const std::string& key_1,
        const std::string& key_2);

    /**
     * key as a array, it's size is not allowable,
     * a suggested size will be tipped and error_message will be displayed if it is set.
     */
    static void
    disallow_array_size(
        const InputDatabase* db,
        const std::string& key,
        const int allowable_size,
        const std::string& error_message);

    /**
     * array's size should be dim while it is not.
     */
    static void
    disallow_non_dim_size(
        const InputDatabase* db,
        const std::string& key);

    /**
     * unrecognized key,
     * the allowable keys are given by allowable_keys.
     */
    static void
    unrecognized_key(
        const InputDatabase* db,
        const std::string& unrecognized_key,
        const std::set<std::string>& allowable_keys);

    /**
     * unrecognized_key, a suggested one will be tipped.
     * and allowable_keys will be displayed.
     */
    static void
    unrecognized_key(
        const InputDatabase* db,
        const std::string& unrecognized_key,
        const std::string& suggested_key,
        const std::set<std::string>& allowable_keys);

    /**
     * error message will be displayed directly.
     */
    static void
    unknown_error(
        const std::string& error_message);

    /**
     * error message will be displayed directly.
     */
    static void
    unknown_error(
        const InputDatabase* db,
        const std::string& error_message);

    /**
     * an unknown error of given key will displayed.
     */
    static void
    unknown_error(
        const InputDatabase* db,
        const std::string& key,
        const std::string& error_message);

    /**
     * file with given name is not found.
     */
    static void
    unfound_file(
        const InputDatabase* db,
        const std::string& key,
        const std::string& file_name);

    /**
     * directory with given name is not found.
     */
    static void
    unfound_directory(
        const InputDatabase* db,
        const std::string& key,
        const std::string& dir_name);

    /**
     * if not build with given library, some function should not been called.
     */
    static void
    unsupported_library(
        const InputDatabase* db,
        const std::string& key,
        const std::string& value,
        const std::string& required_library);

    static void
    unsupported_library(
        const InputDatabase* db,
        const std::string& key,
        const std::string& required_library);

    /**
     * a enum item of key = unrecognized_value will not work if the value is NOT one the the enum's item.
     */
    template<typename T>
    static void
    unrecognized_enum_item(
        const InputDatabase* db,
        const std::string& key,
        const int num);

    /**
     * write all log information to file.
     * the information include all the information in the InfoManager read from setup file,
     * and level set information, simulation system information.
     *
     * no runing information will be saved because it will be writed along with the process.
     *
     * usually this function should be called after all information is prepared.
     * but if FREEMAPS_ERROR() is called, call this function to write log information
     * though the information is not prepared, one can get some information from it.
     */
    static void
    write_log_info_to_file();

    static void
    write_conf_info_to_file(const std::string& conf_file_name);

    /**
     * make the info to have a displacement by filling the prefix.
     * for example, one info may be out to log file by make the blog
     * a tab distance.
     */
    static std::string
    get_log_info (
        const std::ostringstream& info,
        const int num_prefix,
        const bool leave_first_line_unchanged = false,
        const std::string& prefix = " ");

    static std::string
    get_log_info (
        const std::string& info,
        const int num_prefix,
        const bool leave_first_line_unchanged = false,
        const std::string& prefix = " ");

    /**
     * when doing write log file, one often should write the log info like:
     *   key = value_1,
     *         value_2,
     *         ...
     * and the keys are get from a map's key.
     * this output the information for convient.
     */
    template<typename T>
    static std::string
    get_log_info(
        const std::string& key,
        const std::set<T>& value,
        const int num_prefix,
        const bool add_quot_mark);

    /**
     * when doing write log file, one often should write the log info like:
     *   key = value_1,
     *         value_2,
     *         ...
     * and the keys are get from a map's key.
     * this output the information for convient.
     */
    template<typename T>
    static std::string
    get_log_info(
        const std::string& key,
        const std::vector<T>& value,
        const int num_prefix,
        const bool add_quot_mark);

    /**
     * when doing write log file, one often should write the log info like:
     *   key = value_1,
     *         value_2,
     *         ...
     * and the keys are get from a map's key.
     * this output the information for convient.
     */
    template<typename T>
    static std::string
    get_log_info(
        const std::string& key,
        const T* value,
        const int count,
        const int num_prefix,
        const bool add_quot_mark);

    /**
     * write the type of key = all value's keys
     *  key = value_key1,
     *        value_key2,
     *        ...
     */
    template<typename Key, typename Value>
    static std::string
    get_log_info_from_map_keys(
        const std::string& key,
        const std::map<Key, Value>& value,
        const int num_prefix,
        const bool add_quot_mark);

    /**
     * when doing write log file, one often should write the log info like:
     *   key = m_key1,
     *         m_key2,
     *         ...
     *
     *   value = m_value1,
     *           m_value2,
     *          ...
     */
    template<typename Key, typename Value>
    static std::string
    get_log_info_from_map(
        const std::map<Key, Value>& m,
        const int num_prefix,
        const std::string& key,
        const std::string& value,
        const bool add_quot_mark_for_key,
        const bool add_quot_mark_for_value);

    /**
     * output as:
     *   key = [key1, value1],
     *         [key2, value2],
     *         ...
     */
    template<typename Key, typename Value>
    static std::string
    get_log_info_from_map (
        const std::string& key,
        const std::map<Key, Value>& value,
        const int num_prefix,
        const bool add_quot_mark_for_key,
        const bool add_quot_mark_for_value);

    /**
     * output as:
     *   key = "key1",
     *         "key2",
     *         ...
     *   key1 = ...
     *   key2 = ...
     *   ...
     */
    template<typename Value>
    static std::string
    get_log_info_from_map (
        const std::string& key,
        const std::map<std::string, Value*>& value,
        const int num_prefix);

    /**
     * output as:
     *   key = [key, value]
     */
    template<typename Key, typename Value>
    static std::string
    get_log_info_from_pair (
        const std::string& key,
        const std::pair<Key, Value>& value,
        const int num_prefix,
        const bool add_quot_mark_for_key,
        const bool add_quot_mark_for_value);

    /**
     * get the prefex of same symbol with given num.
     */
    static std::string
    get_prefix(
        const int num_prefix,
        const std::string& prefix = " ");

    /**
     * return "TRUE" for true and "FALSE" for false,
     * this is mainly used to output the boolvalue.
     */
    static std::string
    get_bool_string (
        const bool bool_val);

    /**
     * return a string will filling blank before and after the string,
     * this is mainly used to get equal width string for output.
     * if the string's size is large than the width, do nothing
     */
    static std::string
    get_equal_width_string (
        const std::string& str,
        const int width);

    /**
     * similar to the get_equal_width_string, with filling the prefix with given blank.
     */
    static std::string
    get_equal_width_string (
        const std::string& str,
        const int num_prefix,
        const int width);

    /**
     * out put the verbose_info if the info_level is no less than verbose_level.
     * otherwise output nothing.
     * if verbose_level less than 0, then the verbose_level set in CommonInfo will be used.
     */
    static void
    show_verbose_info(
        const std::string& verbose_info);

    static void
    show_message_with_proc_info (
        const std::string& message);

    /**
     * when verbose level is greater than 100, the debug information will be displayed
     */
    static void
    show_function_info(
        const std::string& object_name,
        const std::string& function_name);

private:

    DISALLOW_COPY_AND_ASSIGN (LogMessage);

};

}

#include "COMMON/tbox/LogMessage.inl"

#endif
